package com.beerandjesus.sportofkings.handicapper.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ResultsTableImpl implements ResultsTable {
	
	private Map<String, Integer> columnHeaders = new HashMap<String, Integer>();
	private Map<Integer, Integer> columnWidthBeforeTab = new HashMap<Integer, Integer>();
	private Map<Integer, Map<Integer, String>> table = new HashMap<Integer, Map<Integer,String>>();
	private final Map<Integer, ResultType> postPosResultType = new HashMap<Integer, ResultType>();
	
	private enum DisplayType {
		SIMPLE, BY_TYPE;
	}
	
	public ResultsTableImpl(String[] columnHeaders) {
		int headerIndex = 1;
		Map<Integer, String> headerLine = new HashMap<Integer, String>();
		headerLine.put(0, "PP");
		columnWidthBeforeTab.put(0, 2);
		for (String header : columnHeaders) {
			this.columnHeaders.put(header, headerIndex);
			headerLine.put(headerIndex, header);
			columnWidthBeforeTab.put(headerIndex, header.length());
			headerIndex++;
		}
		table.put(0, headerLine);
	}
	
	/* (non-Javadoc)
	 * @see com.beerandjesus.sportofkings.handicapper.model.ResultTable#putValue(java.lang.Integer, java.lang.String, java.lang.String)
	 */
	public void putValue(Integer postPosition, ResultType resultType, String header, String value) {
		postPosResultType.put(postPosition, resultType);
		Map<Integer, String> data = table.get(postPosition);
		if (data == null) {
			data = new HashMap<Integer, String>();
		}
		Integer index = columnHeaders.get(header);
		if (index == null) {
			throw new RuntimeException("Header '" + header + "' has not been defined");
		}
		data.put(index, value);
		table.put(postPosition, data);
		if (value.length() > columnWidthBeforeTab.get(index)) {
			columnWidthBeforeTab.put(index, value.length());
		}
	}
	
	/* (non-Javadoc)
	 * @see com.beerandjesus.sportofkings.handicapper.model.ResultTable#getTable()
	 */
	public Map<Integer, Map<Integer, String>> getTable() {
		return table;
	}

	public String toStringSimple() {
		return doToString(DisplayType.SIMPLE);
	}
	
	public String toStringByType() {
		return doToString(DisplayType.BY_TYPE);
	}

	private String doToString(DisplayType dType) {
		StringBuffer sb = new StringBuffer();
		
		Map<Integer, String> headers = table.get(0);
		// headers
		int size = headers.keySet().size();
		for (int i = 0; i < size; i++) {
			String header = headers.get(i);
			sb.append(header);
			if (i < size-1) {
				int width = columnWidthBeforeTab.get(i);
				for (int j = header.length(); j < (width + 2); j++) {
					sb.append(" ");
				}
			}
		}
		sb.append("\n");
		// dashes
		for (int i = 0; i < size; i++) {
			int width = columnWidthBeforeTab.get(i);
			for (int j = 0; j < width; j++) {
				sb.append("-");
			}
			if (i < size-1) {
				sb.append("  ");
			}
		}
		sb.append("\n");
		// rows
		StringBuffer rows = new StringBuffer();
		List<Integer> posts;
		switch (dType) {
		case BY_TYPE:
			posts = sortKeysByType(table.keySet());
			break;
		default:
			posts = sortKeys(table.keySet());
		}
		String lastType = ""; 
		for (Integer post : posts) {
			if (dType.equals(DisplayType.BY_TYPE) && post != 0) {
				String type = postPosResultType.get(post).toString();
				if (!lastType.equals(type)) {
					if (!lastType.equals("")) {
						rows.append("\n");
					}
					rows.append(type).append("\n");
					rows.append(sb.toString());
					lastType = type;
				}
			} else if (dType.equals(DisplayType.SIMPLE) && post == 0) {
				rows.append(sb.toString());
			}
			if (post == 0) continue;
			rows.append(post).append("  ");
			if (post < 10) {
				rows.append(" ");
			}
			Map<Integer, String> data = table.get(post);
			for (int i = 1; i <= size; i++) {
				String val = data.get(i);
				if (val != null) {
					rows.append(val);
				} else {
					val = "";
				}
				if (i < size-1) {
					int width = columnWidthBeforeTab.get(i);
					for (int j = val.length(); j < width; j++) {
						rows.append(" ");
					}
					rows.append("  ");
				}
			}
			rows.append("\n");
		}
		
		
		return rows.toString();
	}
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		
		Map<Integer, String> headers = table.get(0);
		int size = headers.keySet().size();
		for (int i = 0; i < size; i++) {
			sb.append(headers.get(i));
			if (i < size-1) {
				sb.append("\t");
			}
		}
		sb.append("\n");
		
		List<Integer> posts = sortKeysByType(table.keySet());
		for (Integer post : posts) {
			if (post == 0) continue;
			sb.append(post).append("\t");
			Map<Integer, String> data = table.get(post);
			for (int i = 1; i <= size; i++) {
				String val = data.get(i);
				if (val != null) {
					sb.append(val);
				}
				if (i < size-1) {
					sb.append("\t");
				}
			}
			sb.append("\n");
		}
		return sb.toString();
	}

	public List<Integer> sortKeysByType(Set<Integer> keys) {
		List<Integer> posts = new ArrayList<Integer>(keys);
		Collections.sort(posts, new Comparator<Integer>() {
			public int compare(Integer o1, Integer o2) {
				if (o1 == 0) {
					return 1;
				} else if (o2 == 0) {
					return -1;
				}
				ResultType t1 = postPosResultType.get(o1);
				ResultType t2 = postPosResultType.get(o2);
				String s1 = new String(t1.toString() + o1.toString());
				String s2 = new String(t2.toString() + o2.toString());
				return s1.compareTo(s2);
			}
			
		});
		return posts;
	}
	
	public List<Integer> sortKeys(Set<Integer> keys) {
		List<Integer> posts = new ArrayList<Integer>(keys);
		Collections.sort(posts, new Comparator<Integer>() {
			public int compare(Integer o1, Integer o2) {
				return o1 - o2;
			}
			
		});
		return posts;
	}
	
	

}
